package ee.serge.util;

import com.google.gwt.core.client.GWT;
import com.google.gwt.event.dom.client.MouseDownEvent;
import com.google.gwt.event.dom.client.MouseDownHandler;
import com.google.gwt.event.shared.GwtEvent;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.event.shared.SimpleEventBus;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.Event.NativePreviewEvent;
import com.google.gwt.user.client.Event.NativePreviewHandler;
import com.google.gwt.user.client.ui.AbsolutePanel;
import com.google.gwt.user.client.ui.FocusPanel;
import com.google.gwt.user.client.ui.SimplePanel;
import com.google.gwt.event.logical.shared.HasValueChangeHandlers;
import com.google.gwt.event.logical.shared.ValueChangeEvent;
import com.google.gwt.event.logical.shared.ValueChangeHandler;
import com.google.gwt.event.shared.EventBus;

public class Slider extends FocusPanel {

   private static final int WIDTH = 400;
   private static final int KNOB_WIDTH = 42;
   private final int max;
   private final int min;
   private SimplePanel knob;
   private AbsolutePanel panel;
   private HandlerRegistration mouseMoveRegistration;
   private ValueChangeClass valueChangeClass;
   private ReleaseSliderClass releaseSliderClass;
   
   public Slider(int min, int max) {
      this.max = max;
      this.min = min;
      valueChangeClass = new ValueChangeClass();
      releaseSliderClass = new ReleaseSliderClass();
      add(panel = new AbsolutePanel());
      setSize(WIDTH + KNOB_WIDTH + "px", "22px");
      panel.setSize(WIDTH + KNOB_WIDTH + "px", "22px");
      panel.setStyleName("slider well");
      knob = new SimplePanel();
      knob.getElement().setAttribute("oncontextmenu", "return false;");
      knob.setStyleName("btn primary knob");
      knob.setSize(KNOB_WIDTH + "px", "20px");
      addMouseDownHandler(new MouseDown());
      panel.add(knob);
   }
   
   public HandlerRegistration addValueChangeHandler(ValueChangeHandler<Integer> handler) {
      return valueChangeClass.addValueChangeHandler(handler);
   }
   
   public HandlerRegistration addReleaseHandler(ValueChangeHandler<Integer> handler) {
      return releaseSliderClass.addValueChangeHandler(handler);
   }
   
   public void setValue(int val) {
      panel.setWidgetPosition(knob, val * WIDTH / max, 0);
   }
   
   private class MouseDown implements MouseDownHandler {

      @Override
      public void onMouseDown(MouseDownEvent event) {
         final int downLoc = event.getRelativeX(knob.getElement());
         mouseMoveRegistration = Event.addNativePreviewHandler(new NativePreviewHandler() {
            @Override
            public void onPreviewNativeEvent(NativePreviewEvent event) {
               switch (event.getTypeInt()) {
               case Event.ONMOUSEUP:
                  mouseMoveRegistration.removeHandler();
                  mouseMoveRegistration = null;
                  int x = (event.getNativeEvent().getClientX() - panel.getAbsoluteLeft());
                  if (x <= downLoc) {
                  	releaseSliderClass.dispatch(min);
                  } else if (x >= WIDTH + KNOB_WIDTH - (KNOB_WIDTH - downLoc)) {
                  	releaseSliderClass.dispatch(max);
                  } else {
                  	releaseSliderClass.dispatch((int) min + ((x - downLoc) * (max - min)) / WIDTH + 1);
                  }
                  break;
               case Event.ONMOUSEMOVE:
                  x = (event.getNativeEvent().getClientX() - panel.getAbsoluteLeft());
                  if (x <= downLoc) {
                     panel.setWidgetPosition(knob, 0, 0);
                     valueChangeClass.dispatch(min);
                  } else if (x >= WIDTH + KNOB_WIDTH - (KNOB_WIDTH - downLoc)) {
                     panel.setWidgetPosition(knob, WIDTH, 0);
                     valueChangeClass.dispatch(max);
                  } else {
                     panel.setWidgetPosition(knob, x - downLoc, 0);
                     valueChangeClass.dispatch((int) min + ((x - downLoc) * (max - min)) / WIDTH + 1);
                  }
                  break;
               }
            }
         });
      }
      
   }

   private class ValueChangeClass implements HasValueChangeHandlers<Integer> {

      private EventBus bus;
      private int prevValue = Integer.MIN_VALUE;
      
      public ValueChangeClass() {
         bus = new SimpleEventBus();
      }
      
      @Override
      public void fireEvent(GwtEvent<?> event) {
         bus.fireEvent(event);
      }

      @Override
      public HandlerRegistration addValueChangeHandler(ValueChangeHandler<Integer> handler) {
         return bus.addHandler(ValueChangeEvent.getType(), handler);
      }
      
      public void dispatch(int value) {
         if (prevValue == value) return;
         ValueChangeEvent.fire(this, value);
         prevValue = value;
      }
      
   }
   
   private class ReleaseSliderClass implements HasValueChangeHandlers<Integer> {

      private EventBus bus;
      private int prevValue = Integer.MIN_VALUE;
      
      public ReleaseSliderClass() {
         bus = new SimpleEventBus();
      }
      
      @Override
      public void fireEvent(GwtEvent<?> event) {
         bus.fireEvent(event);
      }

      @Override
      public HandlerRegistration addValueChangeHandler(ValueChangeHandler<Integer> handler) {
         return bus.addHandler(ValueChangeEvent.getType(), handler);
      }
      
      public void dispatch(int value) {
         if (prevValue == value) return;
         ValueChangeEvent.fire(this, value);
         prevValue = value;
      }
      
   }
      
}
